#include "PreCompile.h"
#include "Base/PluginGenerator.h"
#include "PluginManager.h"

GENERATE_PLUGIN(IPluginManager, PluginManager, PLUGIN_MANAGER_PLUGIN)

// Construct the PluginManager
PluginManager::PluginManager() :
	m_pPool(NULL),
	m_strScanDirPath(""),
	m_strScanTaskId(MyGuid::CreateGUID().ToAnsi()),
	m_strScanTaskName("SCAN_PLUGIN_TASK"),
	m_bDisposed(false)
{
	Initialize();
}

// Detructe the PluginManager
PluginManager::~PluginManager()
{
	Destory();
}

// Initialize the PluginManager
void PluginManager::Initialize()
{
	// Init the scan location
	InitScanLocation();
}

// Destory the PluginManager
void PluginManager::Destory()
{
	if (!GetDisposed())
	{
		SetDisposed(true);

		// Stop the manager
		Stop();
	}
}

// Init the scan location
void PluginManager::InitScanLocation()
{
	String strExeDirPath = Directory::GetExcutableDirectory();

	String strPluginPath = strExeDirPath + PLUGIN_DIRECTORY;

	SetScanDirPath(strPluginPath);
}

// Create module loader 
bool PluginManager::CreateModuleLoader(PluginLoader& pLoader)
{
	pLoader = new ModuleLoader<IPlugin>();

	if (pLoader == NULL)
	{
		return false;
	}

	return true;
}

// Destory module loader
void PluginManager::DestoryModuleLoader(PluginLoader& pLoader)
{
	if (pLoader)
	{
		delete pLoader;

		pLoader = NULL;
	}
}

// Start the manager
bool PluginManager::Start()
{
	if (GetIsRunning())
	{
		return true;
	}

	SetIsRunning(true);

	// Start sacnning task
	StartScanning();

	return true;
}

// Stop the manager
bool PluginManager::Stop()
{
	if (!GetIsRunning())
	{
		return true;
	}

	SetIsRunning(false);

	// Stop the scanning
	StopScanTask();

	// Stop all plugins
	StopAllPlugins();

	// Unload all plugins
	UnloadAllPlugins();

	// Clear the container
	ClearContainer();

	return true;
}

// Set the thread pool
void PluginManager::SetPool(IThreadPool* pPool)
{
	SetThreadPool(pPool);
}

// Get the plugin by name and version
IPlugin* PluginManager::GetPlugin(std::string strPluginName,
	int iMajorVersion,
	int iMinorVersion,
	int iModifyVersion)
{
	if (strPluginName.empty())
	{
		return NULL;
	}

	// Find the plugin loader
	PluginLoader pLoader = NULL;

	if (!GetPlugin(strPluginName, pLoader))
	{
		// Get the plugin path
		String strScanDirPath = GetScanDirPath();

		String strPluginPath = strScanDirPath 
			+ _T("/") 
			+ strPluginName 
			+ _T("/") 
			+ strPluginName
			+_T(".dll");

		// Laod the plugin
		if (!LoadPlugin(strPluginPath))
		{
			return NULL;
		}

		// Search this plugin again
		if (!GetPlugin(strPluginName, pLoader))
		{
			return NULL;
		}
	}

	// Get the plugin
	IPlugin* pPlugin = pLoader->GetModuleInterface();

	if (pPlugin == NULL)
	{
		return NULL;
	}

	// Start the plugin
	if (!StartPlugin(strPluginName))
	{
		return NULL;
	}

	// Check plugin's version compatible
	if (!pPlugin->CheckCompatible(iMajorVersion,iMinorVersion,iModifyVersion))
	{
		String strMsg = _T("Version is not compatible, \
			Current plugin version is:[%d.%d.%d],\
			Your input plugin version is:[%d.%d.%d]");

		int iCurMajorVersion = 0;

		int iCurMinorVersion = 0;

		int iCurModifyVersion = 0;

		pPlugin->GetVersion(iCurMajorVersion, iCurMinorVersion, iCurModifyVersion);

		strMsg.Arg(iCurMajorVersion)
			.Arg(iCurMinorVersion)
			.Arg(iCurModifyVersion)
			.Arg(iMajorVersion)
			.Arg(iMinorVersion)
			.Arg(iModifyVersion);

		LOG_DEBUG_EX(strMsg);

		return NULL;
	}

	return pPlugin;
}

// Release the plugin
bool PluginManager::ReleasePlugin(IPlugin* pPlugin)
{
	if (pPlugin == NULL)
	{
		return false;
	}

	std::string strPluginName = pPlugin->GetName();

	if (!StopPlugin(strPluginName))
	{
		return false;
	}

	if (!UnloadPlugin(strPluginName))
	{
		return false;
	}

	RemovePlugin(strPluginName);

	return true;
}

// Find plugins
bool PluginManager::FindPlugins(String strScanPath, PluginTable& Container)
{
	if (strScanPath.IsEmpty())
	{
		return false;
	}

	String strSearchRootDir = Directory::TrimEnding(strScanPath);

	String strFinalRootDir = strSearchRootDir + _T("/*");

	WIN32_FIND_DATA data;

	HANDLE hDirectory = ::FindFirstFile(strFinalRootDir.CStr(), &data);
	if (hDirectory == INVALID_HANDLE_VALUE)
	{
		return false;
	}

	while (1)
	{
		// Find the next file or directory
		if (!FindNextFile(hDirectory, &data))
		{
			break;
		}

		// Judge the next one is directory or not
		if ((data.dwFileAttributes & DIRECTORY))
		{
			if (String(data.cFileName) == _T("..") || String(data.cFileName) == _T("."))
			{
				continue;
			}

			// Wipe out the last character '*'
			String strRootDir = strFinalRootDir.SubString(0, strFinalRootDir.GetLength() - 1);

			// Get next searching directory path
			String strPluginName = data.cFileName;

			String strSystemDir = Directory::AddEnding(strRootDir) + strPluginName;

			// Find next directory
			if (!FindPlugins(strSystemDir, Container))
			{
				// Close the finding
				::FindClose(hDirectory);

				return false;
			}
		}
		else
		{
			// Plugin file path
			String strPluginFilePath = Directory::AddEnding(strScanPath) + data.cFileName;

			if (strPluginFilePath.IsContain(_T("dll")) || strPluginFilePath.IsContain(_T("DLL")))
			{
				// Add directory name to the map
				Container.insert(strPluginFilePath.ToAnsi());
			}
		}
	}

	// Close the finding
	::FindClose(hDirectory);

	return true;
}

// Searching plugins
bool PluginManager::SearchPlugins(PluginTable& Container)
{
	String strScanPath = GetScanDirPath();

	if (strScanPath.IsEmpty())
	{
		return false;
	}

	if (!FindPlugins(strScanPath, Container))
	{
		return false;
	}

	return true;
}

// Start sacnning task
bool PluginManager::StartScanTask()
{
	if (GetThreadPool() == NULL)
	{
		return false;
	}

	TaskEntity Task;

	Task.strTaskId = GetScanTaskId();

	Task.strTaskName = GetScanTaskName();

	Task.iExcuteIntervalTimeMs = 1000;

	Task.bIsOneTimeTask = true;

	std::shared_ptr<TaskEntity::CallBackFunc> pFunc(new TaskEntity::CallBackFunc);

	*pFunc = std::bind(&PluginManager::StartScanning, this);

	Task.pUserFunc = pFunc;

	if (!GetThreadPool()->AddTask(Task))
	{
		return false;
	}

	return true;
}

// Start scanning available plugins
void PluginManager::StartScanning()
{
	// Searching plugin
	PluginTable PluginPathTable;

	if (SearchPlugins(PluginPathTable))
	{
		// Load all plugins
		if (!PluginPathTable.empty())
		{
			LoadAllPlugins(PluginPathTable);

			StartAllPlugins();
		}
	}
}

// Stop scanning task
void PluginManager::StopScanTask()
{
	if (GetScanTaskId().empty())
	{
		return;
	}

	if (GetThreadPool() == NULL)
	{
		return;
	}

	std::string strScanTaskId = GetScanTaskId();

	GetThreadPool()->StopTask(strScanTaskId);
}

// Load all available plugins
bool PluginManager::LoadAllPlugins(PluginTable& PluginPathTable)
{
	if (PluginPathTable.empty())
	{
		return false;
	}

	for (PluginTable::iterator Iter = PluginPathTable.begin();
		Iter != PluginPathTable.end();
		++Iter)
	{
		String strPluginFilePath = *Iter;

		if (strPluginFilePath.IsEmpty())
		{
			continue;
		}

		LoadPlugin(strPluginFilePath);
	}

	return true;
}

// Start all plugins here
void PluginManager::StartAllPlugins()
{
	std::lock_guard<std::mutex> Locker(m_PluginLock);

	if (m_PluginContainer.empty())
	{
		return;
	}

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.begin();

	IPlugin* pMainPlugin = NULL;

	while (Iter != m_PluginContainer.end())
	{
		PluginLoader pLoader = Iter->second;

		IPlugin* pPlugin = pLoader->GetModuleInterface();

		if (pPlugin->IsStartOnLoad())
		{
			if (!pPlugin->IsMainService())
			{
				StartPlugin(pPlugin);
			}
		}

		++Iter;
	}
}

// Stop all plugins here
void PluginManager::StopAllPlugins()
{
	std::lock_guard<std::mutex> Locker(m_PluginLock);

	if (m_PluginContainer.empty())
	{
		return;
	}

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.begin();

	IPlugin* pMainPlugin = NULL;

	while (Iter != m_PluginContainer.end())
	{
		PluginLoader pLoader = Iter->second;

		IPlugin* pPlugin = pLoader->GetModuleInterface();

		if (pPlugin)
		{
			StopPlugin(pPlugin);
		}

		++Iter;
	}
}

// Unload all available plugins
void PluginManager::UnloadAllPlugins()
{
	std::lock_guard<std::mutex> Locker(m_PluginLock);

	if (m_PluginContainer.empty())
	{
		return;
	}

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.begin();

	IPlugin* pMainPlugin = NULL;

	while (Iter != m_PluginContainer.end())
	{
		PluginLoader pLoader = Iter->second;

		UnloadPlugin(pLoader);

		++Iter;
	}
}

// Clear the container
void PluginManager::ClearContainer()
{
	std::lock_guard<std::mutex> Locker(m_PluginLock);

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.begin();

	while (Iter != m_PluginContainer.end())
	{
		Iter = m_PluginContainer.erase(Iter);
	}

	std::map<std::string, PluginLoader>().swap(m_PluginContainer);
}

// Is plugin existed
bool PluginManager::IsPluginExisted(String strPluginName)
{
	std::lock_guard<std::mutex> Locker(m_PluginLock);

	if (strPluginName.IsEmpty())
	{
		return false;
	}

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.find(strPluginName.ToAnsi());

	if (Iter != m_PluginContainer.end())
	{
		return true;
	}

	return false;
}

// Add plugin
bool PluginManager::AddPlugin(String strPluginName, PluginLoader& pLoader)
{
	if (strPluginName.IsEmpty())
	{
		return false;
	}

	if (pLoader == NULL)
	{
		return false;
	}

	std::lock_guard<std::mutex> Locker(m_PluginLock);

	m_PluginContainer[strPluginName.ToAnsi()] = pLoader;

	return true;
}

// Remove plugin by id
bool PluginManager::RemovePlugin(String strPluginName)
{
	std::lock_guard<std::mutex> Locker(m_PluginLock);

	if (strPluginName.IsEmpty())
	{
		return false;
	}

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.find(strPluginName.ToAnsi());

	if (Iter == m_PluginContainer.end())
	{
		return false;
	}

	m_PluginContainer.erase(Iter);

	return true;
}

// Remove plugin
bool PluginManager::RemovePlugin(IPlugin*& pPlugin)
{
	if (pPlugin == NULL)
	{
		return false;
	}

	if (!RemovePlugin(pPlugin->GetName()))
	{
		return false;
	}

	return true;
}

// Get plugin by id
bool PluginManager::GetPlugin(String strPluginName, PluginLoader& pLoader)
{
	if (strPluginName.IsEmpty())
	{
		return false;
	}

	std::lock_guard<std::mutex> Locker(m_PluginLock);

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.find(strPluginName.ToAnsi());

	if (Iter == m_PluginContainer.end())
	{
		return false;
	}

	pLoader = Iter->second;

	if (pLoader == NULL)
	{
		return false;
	}

	return true;
}

// Get plugin name by path
String PluginManager::GetPluginName(String strFilePath)
{
	if (strFilePath.IsEmpty())
	{
		return _T("");
	}

	String strTempFilePath = strFilePath;

	String strModulePath = String::Replace(strTempFilePath, _T("\\"), _T("/"));

	Int32 iPos = strModulePath.FindLast(_T("/"));
	if (iPos == -1)
	{
		return _T("");
	}

	String strModuleName = strModulePath.Right(strModulePath.GetLength() - iPos - 1);

	Int32 iPos1 = strModuleName.FindLast(_T("."));
	if (iPos1 == -1)
	{
		return _T("");
	}

	String strName = strModuleName.Left(iPos1);

	return strName;
}

// Load plugin and init it
bool PluginManager::LoadPlugin(String strPluginFilePath)
{
	if (strPluginFilePath.IsEmpty())
	{
		return false;
	}

	String strPluginName = GetPluginName(strPluginFilePath);

	if (IsPluginExisted(strPluginName))
	{
		return false;
	}

	PluginLoader pLoader = NULL;

	if (!CreateModuleLoader(pLoader))
	{
		return false;
	}

	if (!pLoader->Load(strPluginFilePath))
	{
		DestoryModuleLoader(pLoader);

		return false;
	}

	IPlugin* pPlugin = pLoader->GetModuleInterface();

	if (pPlugin == NULL)
	{
		DestoryModuleLoader(pLoader);

		return false;
	}

	String strPluginInnerName = pPlugin->GetName();

	LOG_INFORMATION_EX(String(_T("Load plugin:[%s]")).Arg(pPlugin->GetName()));

	if (!AddPlugin(strPluginInnerName, pLoader))
	{
		LOG_ERROR_EX(String(_T("Failed to load plugin:[%s]")).Arg(pPlugin->GetName()));

		DestoryModuleLoader(pLoader);

		return false;
	}

	return true;
}

// Unload plugin
bool PluginManager::UnloadPlugin(PluginLoader& pLoader)
{
	if (pLoader == NULL)
	{
		return false;
	}
	
	IPlugin* pPlugin = pLoader->GetModuleInterface();

	std::string strPluginName = pPlugin->GetName();

	DestoryModuleLoader(pLoader);

	LOG_INFORMATION_EX(String(_T("Unload plugin:[%s]")).Arg(strPluginName));

	return true;
}

// Unload plugin
bool PluginManager::UnloadPlugin(String strPluginName)
{
	PluginLoader pLoader = NULL;

	if (!GetPlugin(strPluginName, pLoader))
	{
		return false;
	}

	std::lock_guard<std::mutex> Locker(m_PluginLock);

	if (!UnloadPlugin(pLoader))
	{
		return false;
	}

	return true;
}

// Start the plugin
bool PluginManager::StartPlugin(String strPluginName)
{
	if (strPluginName.IsEmpty())
	{
		return false;
	}

	PluginLoader pLoader = NULL;

	if (!GetPlugin(strPluginName, pLoader))
	{
		return false;
	}

	std::lock_guard<std::mutex> Locker(m_PluginLock);

	IPlugin* pPlugin = pLoader->GetModuleInterface();

	if (!StartPlugin(pPlugin))
	{
		return false;
	}

	return true;
}

// Stop the plugin
bool PluginManager::StopPlugin(String strPluginName)
{
	if (strPluginName.IsEmpty())
	{
		return false;
	}

	PluginLoader pLoader = NULL;

	if (!GetPlugin(strPluginName, pLoader))
	{
		return false;
	}

	std::lock_guard<std::mutex> Locker(m_PluginLock);

	IPlugin* pPlugin = pLoader->GetModuleInterface();

	if (!StopPlugin(pPlugin))
	{
		return false;
	}

	return true;
}

// Start the plugin
bool PluginManager::StartPlugin(IPlugin*& pPlugin)
{
	if (pPlugin == NULL)
	{
		return false;
	}

	std::string strPluginName = pPlugin->GetName();

	if (!pPlugin->IsRunning())
	{
		LOG_INFORMATION_EX(String(_T("Start plugin:[%s]")).Arg(strPluginName));

		if (!pPlugin->Start())
		{
			LOG_ERROR_EX(String(_T("Failed to start plugin:[%s]")).Arg(strPluginName));

			return false;
		}
	}

	return true;
}

// Stop the plugin
bool PluginManager::StopPlugin(IPlugin*& pPlugin)
{
	if (pPlugin == NULL)
	{
		return false;
	}

	std::string strPluginName = pPlugin->GetName();

	if (pPlugin->IsRunning())
	{
		LOG_INFORMATION_EX(String(_T("Stop plugin:[%s]")).Arg(strPluginName));

		if (!pPlugin->Stop())
		{
			LOG_ERROR_EX(String(_T("Failed to stop plugin:[%s]")).Arg(strPluginName));

			return false;
		}
	}

	return true;
}

// Get main service
bool PluginManager::GetMainService(IPlugin*& pPlugin)
{
	std::lock_guard<std::mutex> Locker(m_PluginLock);

	if (m_PluginContainer.empty())
	{
		return false;
	}

	std::map<std::string, PluginLoader>::iterator Iter = m_PluginContainer.begin();

	IPlugin* pMainPlugin = NULL;

	while (Iter != m_PluginContainer.end())
	{
		PluginLoader pLoader = Iter->second;

		IPlugin* pCurPlugin = pLoader->GetModuleInterface();

		if (pCurPlugin)
		{
			if (pCurPlugin->IsMainService())
			{
				pPlugin = pCurPlugin;

				return true;
			}
		}

		++Iter;
	}

	return false;
}

// Start main service
bool PluginManager::StartMainService()
{
	// Get main service
	IPlugin* pPlugin = NULL;

	if (!GetMainService(pPlugin))
	{
		return false;
	}

	if (!StartPlugin(pPlugin))
	{
		return false;
	}

	return true;
}
